﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ITAP.ContourBuilder.Components.Dimensions;
using System.Drawing;
using ITAP.Core.Calculations;

using ITAP.Core.Modelling.Primitives;
using ITAP.Core.Modelling;

namespace ITAP.ContourBuilder.ContourRender
{
    public class DimensionRender : SimpleContourRender, IDimensionVisitor
    {
        public DimensionRender()
        {
            this.renderPriority = new List<Type>();
            this.renderPriority.Add(typeof(ContourElementDimension)); 
            primitiveRender = new PrimitiveRender();
        }



        #region IDimensionVisitor Members
       
        public virtual void Visit(ContourElementDimension contourElementDimension)
        {
            if (ModelCalculations.IsErrorValue(contourElementDimension.Primitive.Length) || contourElementDimension.Primitive.Length == 0)
                return;

            if ((contourElementDimension.Primitive as IEndedPrimitive).Height != 0)
            {
                RectangleF rect;
                float startAngle;
                float sweepAngle;
                this.GetArcParameters(contourElementDimension.Primitive as Circle, out rect, out startAngle, out sweepAngle);
                graphics.DrawArc(Pens.DarkGray, rect, startAngle, sweepAngle);
            }
            else
            {
                PointF startPoint = this.coordinateConverter.ModelPointToScreenPoint((contourElementDimension.Primitive as IEndedPrimitive).StartPoint, screenWidth, screenHeight, modelRectangle, centerPoint);
                PointF endPoint = this.coordinateConverter.ModelPointToScreenPoint((contourElementDimension.Primitive as IEndedPrimitive).EndPoint, screenWidth, screenHeight, modelRectangle, centerPoint);
                graphics.DrawLine(Pens.DarkGray, startPoint, endPoint);
            }

            PointF dimensionStartPoint = this.coordinateConverter.ModelPointToScreenPoint((contourElementDimension.Primitive as IEndedPrimitive).StartPoint, screenWidth, screenHeight, modelRectangle, centerPoint);
            PointF dimensionEndPoint = this.coordinateConverter.ModelPointToScreenPoint((contourElementDimension.Primitive as IEndedPrimitive).EndPoint, screenWidth, screenHeight, modelRectangle, centerPoint);

            PointF elementStartPoint = this.coordinateConverter.ModelPointToScreenPoint(contourElementDimension.Element.StartPoint, screenWidth, screenHeight, modelRectangle, centerPoint);
            PointF elementEndPoint = this.coordinateConverter.ModelPointToScreenPoint(contourElementDimension.Element.EndPoint, screenWidth, screenHeight, modelRectangle, centerPoint);

            graphics.DrawLine(Pens.DarkGray, dimensionStartPoint, elementStartPoint);
            graphics.DrawLine(Pens.DarkGray, dimensionEndPoint, elementEndPoint);

            this.DrawDimensionText(contourElementDimension);

        }

        #endregion

        #region IDimensionVisitor Members


        public virtual void Visit(ArcHeightDimension arcHeightDimension)
        {
            if (arcHeightDimension.Element.Height == 0)
                return;

            PointF startPoint = this.coordinateConverter.ModelPointToScreenPoint((arcHeightDimension.Primitive as IEndedPrimitive).StartPoint, screenWidth, screenHeight, modelRectangle, centerPoint);
            PointF endPoint = this.coordinateConverter.ModelPointToScreenPoint((arcHeightDimension.Primitive as IEndedPrimitive).EndPoint, screenWidth, screenHeight, modelRectangle, centerPoint);
            graphics.DrawLine(Pens.DarkGray, startPoint, endPoint);

            this.DrawDimensionText(arcHeightDimension);
        }

        #endregion

        #region IDimensionVisitor Members

        private void DrawDimensionText(Dimension dimension, PointF screenPointToDrawString, bool centered)
        {
            SizeF fontSize = graphics.MeasureString(dimension.DimensionText, dimension.Font);

            float textModelWidth =  coordinateConverter.ScreenMeasureToModelMeasure(fontSize.Width, screenWidth, screenHeight, modelRectangle);
            float textModelHeight = coordinateConverter.ScreenMeasureToModelMeasure(fontSize.Height, screenWidth, screenHeight, modelRectangle);
            dimension.TextModelWidth = textModelWidth;

            ModelVector vector = dimension.GetSizeLineNormal();
            Angle angle = vector.GetAngleBetweenAbscissa(false);

            //ModelVector normal = vector.Rotate(new Angle(-Math.PI / 2));            
            
            float _angle;
            float textDirectionOffset = 0;
            ModelVector _vector = vector.Rotate(new Angle(Math.PI / 2));
            if (!dimension.ChangeTextDirection)
            {
                
                _angle = (float)(-angle.Value * 180 / Math.PI) - 90;
                vector = vector.Rotate(new Angle(Math.PI));

            }
            else
            {
                textDirectionOffset = fontSize.Width;            
                _angle = (float)(-angle.Value * 180 / Math.PI) - 270;
                
            }

            ModelPoint modelPointToDrawString = coordinateConverter.ScreenPointToModelPoint
                (screenPointToDrawString, screenWidth, screenHeight, modelRectangle, centerPoint);

            
            _vector.Length = textModelWidth / 2;
            // Просчёт центральных точек надписей размеров.
            if (centered)
            {

                vector.Length = textModelHeight / 2;
                ModelPoint centerTextPoint = ModelCalculations.GetOffsetPoint(modelPointToDrawString, vector);
                dimension.TextCenterPoint = centerTextPoint;
                //PointF scrPoint = this.coordinateConverter.ModelPointToScreenPoint(centerTextPoint, screenWidth, screenHeight, modelRectangle, centerPoint);
                //graphics.DrawEllipse(Pens.Red, scrPoint.X - 2, scrPoint.Y - 2, 4, 4);
            }
            else
            {                
                vector.Length = textModelHeight / 2;
                ModelPoint centerTextPoint = ModelCalculations.GetOffsetPoint(modelPointToDrawString, vector);
                centerTextPoint = ModelCalculations.GetOffsetPoint(centerTextPoint, _vector);
                dimension.TextCenterPoint = centerTextPoint;
                //PointF scrPoint = this.coordinateConverter.ModelPointToScreenPoint(centerTextPoint, screenWidth, screenHeight, modelRectangle, centerPoint);
                //graphics.DrawEllipse(Pens.Red, scrPoint.X - 2, scrPoint.Y - 2, 4, 4);
            }

            graphics.TranslateTransform(screenPointToDrawString.X, screenPointToDrawString.Y);
            graphics.RotateTransform(_angle);

            Brush brush;
            if (dimension.Selected)
                brush = Brushes.Green;
            else
                brush = Brushes.Black;

            if (centered)
            {
                PointF textLocation = new PointF(-fontSize.Width / 2, -fontSize.Height);
                graphics.DrawString(dimension.DimensionText, dimension.Font, brush, textLocation);
            }
            else
            {
                PointF textLocation = new PointF(-textDirectionOffset, -fontSize.Height);
                graphics.DrawString(dimension.DimensionText, dimension.Font, brush, textLocation);
            }

            graphics.RotateTransform(-_angle);
            graphics.TranslateTransform((float)(-screenPointToDrawString.X), -screenPointToDrawString.Y);            
        }

        private void DrawDimensionText(Dimension dimension)
        {

            PointF screenPointToDrawString =
               coordinateConverter.ModelPointToScreenPoint(dimension.Primitive.GetPoint(0.5), screenWidth, screenHeight, modelRectangle, this.centerPoint);

            this.DrawDimensionText(dimension, screenPointToDrawString, true);
        }

        public virtual void Visit(ElementLeaderDimension leaderDimension)
        {            
            this.DrawLeaderDimension(leaderDimension, leaderDimension.Element.StartPoint, leaderDimension.Element.EndPoint);
            this.DrawDimensionText(leaderDimension);
        }

        public virtual void Visit(ArcRadiusDimension2 arcRadiusDimension)
        {
            PointF startPoint = coordinateConverter.ModelPointToScreenPoint(arcRadiusDimension.EndedPrimitive.StartPoint, screenWidth, screenHeight, modelRectangle, centerPoint);
            PointF endPoint = coordinateConverter.ModelPointToScreenPoint(arcRadiusDimension.EndedPrimitive.EndPoint, screenWidth, screenHeight, modelRectangle, centerPoint);

            graphics.DrawLine(Pens.DarkGray, startPoint, endPoint);
            graphics.FillEllipse(Brushes.DarkGray, endPoint.X - 2, endPoint.Y - 2, 4, 4);
            this.DrawDimensionText(arcRadiusDimension);
        }

        #endregion

        #region IDimensionVisitor Members


        public void Visit(ComponentLeaderDimension leaderDimension)
        {
            this.DrawLeaderDimension(leaderDimension, leaderDimension.Component1.Position, leaderDimension.Component2.Position);
            this.DrawDimensionText(leaderDimension);
        }

        #endregion

        protected void DrawLeaderDimension(ElementLeaderDimension leaderDimension, ModelPoint point1, ModelPoint point2)
        {
            RectangleF bbox = leaderDimension.Primitive.BoundingBox;

            if (float.IsNaN(bbox.X) || float.IsNaN(bbox.Y) || float.IsNaN(bbox.Width) || float.IsNaN(bbox.Height))
                return;
            ModelPoint leaderStartPointModel = leaderDimension.EndedPrimitive.StartPoint;
            ModelPoint leaderEndPointModel = leaderDimension.EndedPrimitive.EndPoint;

            PointF leaderStartPointScreen = coordinateConverter.ModelPointToScreenPoint(leaderStartPointModel, screenWidth, screenHeight, modelRectangle, centerPoint);
            PointF leaderEndPointScreen = coordinateConverter.ModelPointToScreenPoint(leaderEndPointModel, screenWidth, screenHeight, modelRectangle, centerPoint);

            PointF startPointScreen = coordinateConverter.ModelPointToScreenPoint(point1, screenWidth, screenHeight, modelRectangle, centerPoint);
            PointF endPointScreen = coordinateConverter.ModelPointToScreenPoint(point2, screenWidth, screenHeight, modelRectangle, centerPoint);

            if (leaderDimension.LeaderType == LeaderDimensionType.Horizontal)
            {
                if (SimpleEqualProvider.IsEqual(leaderStartPointModel.Y, point1.Y, 0.01))
                {
                    graphics.DrawLine(Pens.DarkGray, startPointScreen, leaderStartPointScreen);
                    graphics.DrawLine(Pens.DarkGray, endPointScreen, leaderEndPointScreen);
                }
                else
                {
                    graphics.DrawLine(Pens.DarkGray, startPointScreen, leaderEndPointScreen);
                    graphics.DrawLine(Pens.DarkGray, endPointScreen, leaderStartPointScreen);
                }
            }
            else
            {
                if (SimpleEqualProvider.IsEqual(leaderStartPointModel.X, point1.X, 0.01))
                {
                    graphics.DrawLine(Pens.DarkGray, startPointScreen, leaderStartPointScreen);
                    graphics.DrawLine(Pens.DarkGray, endPointScreen, leaderEndPointScreen);
                }
                else
                {
                    graphics.DrawLine(Pens.DarkGray, startPointScreen, leaderEndPointScreen);
                    graphics.DrawLine(Pens.DarkGray, endPointScreen, leaderStartPointScreen);
                }
            }

            graphics.DrawLine(Pens.DarkGray, leaderStartPointScreen, leaderEndPointScreen);
        }

        #region IDimensionVisitor Members


        public void Visit(DiameterDimension diameterDimension)
        {            
            SizeF fontSize = graphics.MeasureString(diameterDimension.DimensionText, diameterDimension.Font);

            ModelPoint startDiameterLinePoint = diameterDimension.StartDiameterLinePoint;
            ModelPoint endDiameterLinePoint = diameterDimension.Position;

            PointF startPointScreen = coordinateConverter.ModelPointToScreenPoint(startDiameterLinePoint, screenWidth, screenHeight, modelRectangle, centerPoint);
            PointF endPointScreen = coordinateConverter.ModelPointToScreenPoint(endDiameterLinePoint, screenWidth, screenHeight, modelRectangle, centerPoint);

            graphics.DrawLine(Pens.DarkGray, startPointScreen, endPointScreen);

            if (diameterDimension.Type == DiameterDimensionType.Right)
            {
                this.DrawDimensionText(diameterDimension, endPointScreen, false);
                graphics.DrawLine(Pens.DarkGray, endPointScreen, new PointF(endPointScreen.X + fontSize.Width, endPointScreen.Y));
            }
            else
            {
                this.DrawDimensionText(diameterDimension, new PointF(endPointScreen.X - fontSize.Width, endPointScreen.Y), false);
                graphics.DrawLine(Pens.DarkGray, endPointScreen, new PointF(endPointScreen.X - fontSize.Width, endPointScreen.Y));
            }

            
        }

        #endregion

        #region IDimensionVisitor Members


        public void Visit(RemoteRadiusDimension radiusDimension)
        {
            if(radiusDimension.Element.Height == 0)
                return;

            ModelPoint center = (radiusDimension.Element.Primitive as Circle).Center;
            ModelPoint startPoint = radiusDimension.Element.Primitive.GetPoint(0.5);
            PointF screenCenter = coordinateConverter.ModelPointToScreenPoint(center, screenWidth, screenHeight, modelRectangle, centerPoint);
            SizeF fontSize = graphics.MeasureString(radiusDimension.DimensionText, radiusDimension.Font);
            float modelFontWidth = coordinateConverter.ScreenMeasureToModelMeasure(fontSize.Width, screenWidth, screenHeight, modelRectangle);
            float offsetInModel = coordinateConverter.ScreenMeasureToModelMeasure(radiusDimension.OffsetFromHoleInPixels, screenWidth, screenHeight, modelRectangle);
            ModelVector vector = new ModelVector(center, startPoint);
            vector.Length = modelFontWidth + offsetInModel;
            ModelPoint modelPointToDraw = ModelCalculations.GetOffsetPoint(startPoint, vector);

            PointF screenModelPointToDraw = coordinateConverter.ModelPointToScreenPoint(modelPointToDraw, screenWidth, screenHeight, modelRectangle, centerPoint);
            graphics.DrawLine(Pens.DarkGray, screenCenter, screenModelPointToDraw);

            if (radiusDimension.Element.Height > 0)
            {
                vector.Length = offsetInModel;
                ModelPoint startPointOffset = ModelCalculations.GetOffsetPoint(startPoint, vector);
                PointF startPointScreen =
                    coordinateConverter.ModelPointToScreenPoint(startPointOffset, screenWidth, screenHeight, modelRectangle, centerPoint);
                this.DrawDimensionText(radiusDimension, startPointScreen, false);
            }
            else
            {
                vector.Length = offsetInModel + modelFontWidth;
                ModelPoint startPointOffset = ModelCalculations.GetOffsetPoint(startPoint, vector);
                PointF startPointScreen =
                    coordinateConverter.ModelPointToScreenPoint(startPointOffset, screenWidth, screenHeight, modelRectangle, centerPoint);
                this.DrawDimensionText(radiusDimension, startPointScreen, false);
            }
        }

        #endregion

        #region IDimensionVisitor Members


        public void Visit(DirectDistanceDimension distanceDimension)
        {
            PointF startPoint = this.coordinateConverter.ModelPointToScreenPoint(distanceDimension.Component1.Position, screenWidth, screenHeight, modelRectangle, centerPoint);
            PointF endPoint = this.coordinateConverter.ModelPointToScreenPoint(distanceDimension.Component2.Position, screenWidth, screenHeight, modelRectangle, centerPoint);
            graphics.DrawLine(Pens.DarkGray, startPoint, endPoint);
            this.DrawDimensionText(distanceDimension);
        }

        #endregion

        #region IDimensionVisitor Members


        public void Visit(OffsetFromMainBorderDimension offsetDimension)
        {

            IEndedPrimitive primitive = offsetDimension.Primitive as IEndedPrimitive;
            PointF startPointScreen = this.coordinateConverter.ModelPointToScreenPoint(primitive.StartPoint, screenWidth, screenHeight, modelRectangle, centerPoint);
            PointF endPointScreen = this.coordinateConverter.ModelPointToScreenPoint(primitive.EndPoint, screenWidth, screenHeight, modelRectangle, centerPoint);

            PointF centerPointScreen = new PointF((startPointScreen.X + endPointScreen.X) / 2, (startPointScreen.Y + endPointScreen.Y) / 2);

            graphics.DrawLine(Pens.DarkGray, startPointScreen, endPointScreen);

            SizeF stringSize = graphics.MeasureString(offsetDimension.DimensionText, offsetDimension.Font);

            if (offsetDimension.Type == OffsetFromMainBorderDimensionType.Up ||
                offsetDimension.Type == OffsetFromMainBorderDimensionType.Down)
            {
                graphics.DrawLine(Pens.DarkGray, new PointF(startPointScreen.X - 5, startPointScreen.Y), new PointF(startPointScreen.X + 5, startPointScreen.Y));
                graphics.DrawLine(Pens.DarkGray, new PointF(endPointScreen.X - 5, endPointScreen.Y), new PointF(endPointScreen.X + 5, endPointScreen.Y));
            }
            else
            {
                graphics.DrawLine(Pens.DarkGray, new PointF(startPointScreen.X, startPointScreen.Y - 5), new PointF(startPointScreen.X, startPointScreen.Y + 5));
                graphics.DrawLine(Pens.DarkGray, new PointF(endPointScreen.X, endPointScreen.Y - 5), new PointF(endPointScreen.X, endPointScreen.Y + 5));

            }

            float leaderLenghtInPixels = this.coordinateConverter.ModelMeasureToScreenMeasure((float)offsetDimension.LeaderLength, screenWidth, screenHeight, modelRectangle);

            if (offsetDimension.Type == OffsetFromMainBorderDimensionType.Up || 
                offsetDimension.Type == OffsetFromMainBorderDimensionType.Left)
            {
                PointF endLeaderPointScreen = new PointF(centerPointScreen.X - leaderLenghtInPixels,
                                                         centerPointScreen.Y - leaderLenghtInPixels);
                PointF startLeaderPointScreen = new PointF(endLeaderPointScreen.X - stringSize.Width, endLeaderPointScreen.Y);
                graphics.DrawLine(Pens.DarkGray, startLeaderPointScreen, endLeaderPointScreen);
                graphics.DrawLine(Pens.DarkGray, endLeaderPointScreen, centerPointScreen);
                this.DrawDimensionText(offsetDimension, startLeaderPointScreen, false);
            }
            else
            {
                PointF endLeaderPointScreen = new PointF(centerPointScreen.X + leaderLenghtInPixels,
                                                         centerPointScreen.Y + leaderLenghtInPixels);
                PointF startLeaderPointScreen = new PointF(endLeaderPointScreen.X + stringSize.Width, endLeaderPointScreen.Y);
                graphics.DrawLine(Pens.DarkGray, startLeaderPointScreen, endLeaderPointScreen);
                graphics.DrawLine(Pens.DarkGray, endLeaderPointScreen, centerPointScreen);
                this.DrawDimensionText(offsetDimension, endLeaderPointScreen, false);
            }


            
        }

        #endregion
    }
}
